/*
 * Copyright 2009 original author or authors. Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and limitations under the
 * License.
 */
package cn.configleon.configurer;

import cn.configleon.password.encrypt.Encryptor;
import cn.configleon.resource.PropertyResources;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;


/**
 * A special properties configurator that loads the Spring property files based on a JVM parameter.
 */
public abstract class AbstractPropertyConfigurer extends PropertyPlaceholderConfigurer implements InitializingBean {

    private static Logger logger = LoggerFactory.getLogger(AbstractPropertyConfigurer.class);

    private String jvmVariableName = "env";

    private PropertyResources propertyResources;

    /**
     * After the bean factory loads then configure project specific settings.
     */
    public void afterPropertiesSet() throws Exception {

        String env = System.getProperty(jvmVariableName);

        if (!StringUtils.hasText(env)) {
            String msg = "The '" + jvmVariableName
                    + "' variable is not specified in the JVM settings! Using default value env=dev";
            env = "open-api";
            logger.info(msg);
        }

        if (!Environment.validate(env)) {
            throw new RuntimeException("Env parameter: " + env + " is not valid");
        }

        if (logger.isInfoEnabled()) {
            logger.info("The '" + jvmVariableName + "' JVM variable is " + env);
        }

        Properties properties = loadProperties(env);
        postConfiguration(properties);

//        InputStream inputStream = Object.class.getResourceAsStream("config-default.properties");
//
//        Properties properties = new Properties();
//        properties.load(inputStream);
//        setProperties(properties);
//        Config.setProperties(properties);
    }

    protected Properties loadProperties(String configLocation) throws IOException {
        Properties properties = new Properties();
        List<Resource> resources = propertyResources.getResources(configLocation);
        for (Resource resource : resources) {
            properties.load(resource.getInputStream());
            logger.info("Loading resource " + resource.getFilename());
        }
        setProperties(properties);

        Config.setProperties(properties);

        logger.info("");
        logger.info("------------------------------Properties start---------------------------------");
        for (Object key : sortedKeys(properties)) {
            String value = (String) properties.get(key);
            String newValue = value;

            if (value.matches("ENC(.*)")) {
                newValue = newValue.substring(4, newValue.length());
                newValue = Encryptor.decrypt(newValue);
            }
            logger.info(key + " --> " + newValue);
            properties.setProperty((String) key, newValue);
        }
        logger.info("------------------------------Properties end---------------------------------");
        logger.info("");
        return properties;
    }

    protected void postConfiguration(Properties properties) {
        // by default does not do anything
    }

    /**
     * Sort the property keys so that it is easier to debug.
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private List sortedKeys(Properties properties) {
        Set keys = properties.keySet();
        List sortedKeys = new ArrayList(keys);
        Collections.sort(sortedKeys);
        return sortedKeys;
    }

    protected PropertyResources getPropertyResources() {
        return propertyResources;
    }

    public void setPropertyResources(PropertyResources propertyResources) {
        this.propertyResources = propertyResources;
    }

    protected String getJvmVariableName() {
        return jvmVariableName;
    }

    /**
     * Allow the jvm variable name to be configured (by Spring)
     */
    public void setJvmVariableName(String jvmVariableName) {
        this.jvmVariableName = jvmVariableName;
    }
}
